package org.summerframework.component.security.session.websocket.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.summerframework.component.security.Constants;
import org.summerframework.component.security.session.websocket.interceptor.UserHandshakeInterceptor;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.web.socket.config.annotation.AbstractSessionWebSocketMessageBrokerConfigurer;
import org.springframework.session.web.socket.server.SessionRepositoryMessageInterceptor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.StompWebSocketEndpointRegistration;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import javax.annotation.Resource;
import java.util.List;

@Configuration
@EnableConfigurationProperties(WebsocketConfiguration.WebsocketPropertie.class)
@EnableWebSocketMessageBroker
@ConditionalOnBean(SessionRepository.class)
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class WebsocketConfiguration extends AbstractSessionWebSocketMessageBrokerConfigurer<Session> {

    @Resource
    private SessionRepository<Session> sessionRepository;

    @Resource
    private MappingJackson2MessageConverter mappingJackson2MessageConverter;

    @Resource
    private List<HandshakeInterceptor> handshakeInterceptors;

    @Resource
    private List<ChannelInterceptor> channelInterceptors;

    @Resource
    private WebsocketPropertie websocketPropertie;

    @Getter
    @Setter
    @ConfigurationProperties("spring.websocket")
    public class WebsocketPropertie {
        private String[] endpoint = new String[]{"/message"};
        private String[] allowedOrigins = new String[]{"*"};
        private long[] heartbeat = new long[]{10000, 10000};
        private Boolean withSockJs = true;
        private Boolean heartbeatRefreshSession = false;
    }

    @Bean
    @ConditionalOnMissingBean(MappingJackson2MessageConverter.class)
    public MappingJackson2MessageConverter mappingJackson2MessageConverter(ObjectMapper objectMapper) {
        MappingJackson2MessageConverter mappingJackson2MessageConverter = new MappingJackson2MessageConverter();
        mappingJackson2MessageConverter.setObjectMapper(objectMapper);
        return mappingJackson2MessageConverter;
    }

    /**
     * 配置webSocket请求
     * <p>
     * 请求路径 {@link StompEndpointRegistry#addEndpoint(String...)}
     * <p>
     * 安全 {@link StompWebSocketEndpointRegistration#setAllowedOrigins(String...)}
     * <p>
     * 请求过程的拦截器 {@link StompWebSocketEndpointRegistration#addInterceptors(HandshakeInterceptor...)}
     *
     * @param registry stomp 注册中心
     */
    @Override
    public void configureStompEndpoints(StompEndpointRegistry registry) {
        AnnotationAwareOrderComparator.sort(handshakeInterceptors);
        handshakeInterceptors.remove(sessionRepositoryInterceptor());
        StompWebSocketEndpointRegistration stompWebSocketEndpointRegistration = registry.addEndpoint(websocketPropertie.getEndpoint())
                .setAllowedOrigins(websocketPropertie.getAllowedOrigins())
                .addInterceptors(handshakeInterceptors.toArray(new HandshakeInterceptor[0]));

        if (websocketPropertie.withSockJs) {
            stompWebSocketEndpointRegistration.withSockJS();
        }
    }

    /**
     * 配置webSocket 注册路径
     * <p>
     * enableSimpleBroker 注册路径,如果需要发送给用户,必须添加与{@link MessageBrokerRegistry#setUserDestinationPrefix(String)}一直的参数
     * setApplicationDestinationPrefixes 客户端发送的前缀 {@link MessageBrokerRegistry#setApplicationDestinationPrefixes(String...)}
     * setUserDestinationPrefix 用户前缀 {@link MessageBrokerRegistry#setUserDestinationPrefix(String)}
     *
     * @param registry 注册
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker(Constants.WEBSOCKET_QUEUE_PREFIXES, Constants.WEBSOCKET_TOPIC_PREFIXES, Constants.WEBSOCKET_USER_PREFIXES)
                .setTaskScheduler(new DefaultManagedTaskScheduler())
                .setHeartbeatValue(websocketPropertie.getHeartbeat());
        registry.setApplicationDestinationPrefixes("/app", Constants.WEBSOCKET_USER_PREFIXES);
        registry.setUserDestinationPrefix(Constants.WEBSOCKET_USER_PREFIXES);
        registry.configureBrokerChannel().interceptors(userHandshakeInterceptor(websocketPropertie));
    }

    /**
     * 开启webSocket时, 默认创建httpSession
     *
     * @return {@link HttpSessionHandshakeInterceptor}
     */
    @Bean
    public HttpSessionHandshakeInterceptor httpSessionHandshakeInterceptor() {
        HttpSessionHandshakeInterceptor httpSessionHandshakeInterceptor = new HttpSessionHandshakeInterceptor();
        httpSessionHandshakeInterceptor.setCreateSession(true);

        return httpSessionHandshakeInterceptor;
    }

    /**
     * session管理器,
     *
     * @return {@link SessionRepositoryMessageInterceptor}
     */
    @Override
    public SessionRepositoryMessageInterceptor<Session> sessionRepositoryInterceptor() {
        return new SessionRepositoryMessageInterceptor<Session>(sessionRepository);
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        super.configureWebSocketTransport(registration);
    }

    /**
     * websocket 添加用户信息
     * <p>
     * 进入webSocket {@link ChannelRegistration#interceptors(ChannelInterceptor...)}
     *
     * @param registration {@link ChannelRegistration}
     */
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        AnnotationAwareOrderComparator.sort(channelInterceptors);
        channelInterceptors.remove(sessionRepositoryInterceptor());
        registration.interceptors(channelInterceptors.toArray(new ChannelInterceptor[0]));
//        registration.taskExecutor(threadPoolTaskExecutor());
    }

    /**
     * 用户拦截, 设置用户
     *
     * @return {@link UserHandshakeInterceptor}
     */
    @Bean
    public UserHandshakeInterceptor userHandshakeInterceptor(WebsocketPropertie websocketPropertie) {
        return new UserHandshakeInterceptor<>(sessionRepository, websocketPropertie.getHeartbeatRefreshSession());
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration registration) {
//        registration.taskExecutor(threadPoolTaskExecutor());
        registration.interceptors(userHandshakeInterceptor(websocketPropertie));
    }

    private static ThreadPoolTaskExecutor taskExecutor;

    private synchronized ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        if (taskExecutor == null) {
            taskExecutor = new ThreadPoolTaskExecutor();
            taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
            taskExecutor.setAllowCoreThreadTimeOut(true);
            return taskExecutor;
        } else {
            return taskExecutor;
        }
    }

    /**
     * 增加对json的支持
     *
     * @param messageConverters 消息转换器列表
     * @return 是否使用获取的消息转换器
     */
    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
        messageConverters.add(mappingJackson2MessageConverter);
        return super.configureMessageConverters(messageConverters);
    }
}
